/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package hermit.forum.api;

import hermit.forum.db.CreateDB;
import hermit.forum.db.DB;
import hermit.forum.db.Table;
import hermit.forum.tables.Forum;
import hermit.forum.tables.Post;
import hermit.forum.tables.Topic;
import hermit.forum.tables.User;
import hermit.forum.tables.Сategory;
import hermit.forum.log.Log;
import java.io.File;
import java.io.FileFilter;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 *
 * @author Redin Pavel (hermit)
 */
public class ForumCore {
    public static final int STATE_NORMAL = 0;
    public static final int STATE_HIDDEN = 1;
    public static final int STATE_MODERATION = 2;
    public static final int STATE_REMOVED = 3;
    
    public static final int USER_STATE_BANNED = -1;
    public static final int USER_STATE_NO_REG = 0;
    public static final int USER_STATE_NO_FORUM = 1;
    public static final int USER_STATE_FORUM = 2;


    private FileFilter filefilter = new FileFilter() {
        public boolean accept(File file) {
            if (file.isFile()&&file.getName().toLowerCase().endsWith(".db")) {
                return true;
            }
            return false;
        }
    };

    private ConcurrentHashMap<String,DB> db;
    private Random r = new Random();
    //Кэш для базы юзеров
    private ConcurrentHashMap <String,User> uc;
    // Индекс ид-уин
    private ConcurrentHashMap <Integer,String> uu;
    //Кэш категорий
    private LinkedHashMap <Integer,Сategory> C = new LinkedHashMap <Integer,Сategory>();
    //Кэш форумов
    private LinkedHashMap <Integer,Forum> F = new LinkedHashMap <Integer,Forum>();
    //Кэш тем
    private LinkedHashMap <Integer,Topic> T = new LinkedHashMap <Integer,Topic>();
    //Кэш сообщений
    private ConcurrentHashMap <Integer,Post> P = new ConcurrentHashMap <Integer,Post>();
    public String dbPatch ="";
    private Log logger;
    private int CountC=0;
    private int CountF=0;
    private int CountT=0;
    private int CountP=0;
    private int CountU=0;
    private CreateDB CDB;



    public ForumCore(String dbPatch) {
       logger = new Log();
       this.dbPatch = (dbPatch.endsWith(File.separator)) ? dbPatch : dbPatch+File.separator;
       db = new ConcurrentHashMap<String,DB>();
       uc = new ConcurrentHashMap<String,User>();
       uu = new ConcurrentHashMap<Integer,String>();
       CDB = new CreateDB() ;
       init();
   }



   private void init(){
     CDB.skanDB(this);
     initDB();
     fillCashС();
     fillCashF();
     fillCashT();
     logger.out("Пришел в сознание ;)");
   }

    /**
     *Инициализация доступных баз
     */
    public void initDB() {
       connectDB("categories",new File(dbPatch+"categories.db"));
       connectDB("forums",new File(dbPatch+"forums.db"));
       connectDB("posts",new File(dbPatch+"posts.db"));
       connectDB("topics",new File(dbPatch+"topics.db"));
       connectDB("users",new File(dbPatch+"users.db"));
       connectDB("log",new File(dbPatch+"log.db"));
        try {
             for (File file_db : new File(dbPatch).listFiles(filefilter)){
               connectDB(file_db);
             }
        } catch (Exception ex) {
            logger.err(ex.getMessage());
        }
    }

    /**
     *Инициализация определенной базы
     */
    public DB connectDB(File file_db) {
       String name = file_db.getName();
       return connectDB(name.substring(0, name.lastIndexOf(".")), file_db);
    }

    /**
     *Инициализация определенной базы
     */
    public DB connectDB(String name, File file_db) {
       DB temp_db = null;
       if (!db.containsKey(name))
       try {
            // File file_db = new File(dbPatch+file);
             temp_db = new DB(logger);
             temp_db.openConnection(file_db);
             db.put(name,temp_db);
         //    logger.db("connected to: " + name);
        } catch (Exception ex) {
            ex.printStackTrace();
            //logger.err(ex.getMessage());
        }
       return temp_db;
    }


   /**
    * возвращает logger
    * @return logger
    */
   public Log getLogger(){
       return logger;
   }

   /**
    * возвращает кол-во записей в базе
    * @param nameDb имя базы (оно-же имя таблицы)
    * @param query sql запрос (если требуются доп. условия)
    * @return
    */
   public int getCount(String nameDb, String query) {
        int i=0;
        if(query.isEmpty()) query="SELECT count(*) FROM "+nameDb;
        try {
            PreparedStatement pst = db.get(nameDb).getDb().prepareStatement(query);
            ResultSet rs = pst.executeQuery();
            if(rs.next()){
                i = rs.getInt(1);
            }
            rs.close();
            pst.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return i;
   }

   /**
    * возвращает кол-во категорий
    * @return
    */
   public int getCountСategories() {
        if (CountC==0)
        CountC = getCount("categories", "");
        return CountC;
   }

   /**
    * возвращает кол-во форумов
    * @return
    */
   public int getCountForums() {
        if (CountF==0)
        CountF = getCount("forums", "");
        return CountF;
   }

   /**
    * возвращает кол-во форумов определенной категорий
    * @param category_id ид категории
    * @return
    */
   public int getCountForums(int category_id) {
        return getCount("forums", "SELECT count(*) FROM forums WHERE category_id = "+category_id);
   }

   /**
    * возвращает кол-во тем
    * @return
    */
   public int getCountTopics() {
        if (CountT==0)
        CountT = getCount("topics", "");
        return CountT;
   }

   /**
    * возвращает кол-во тем определённого форума
    * @param forum_id ид форума
    * @return
    */
   public int getCountTopics(int forum_id) {
        return getCount("topics", "SELECT count(*) FROM topics WHERE forum_id = "+forum_id);
   }

   /**
    * возвращает кол-во сообщений
    * @return
    */
   public int getCountPosts() {
        if (CountP==0)
        CountP = getCount("posts", "");
        return CountP;
   }

   /**
    * возвращает кол-во пользователей
    * @return
    */
   public int getCountUsers() {
        if (CountU==0)
        CountU = getCount("users", "");
        return CountU;
   }
   /**
    * возвращает кол-во сообщений определённой темы
    * @param topic_id ид темы
    * @return
    */
   public int getCountPosts(int topic_id) {
        return getCount("posts", "SELECT count(*) FROM posts WHERE topic_id = "+topic_id);
   }

    /**
     * Заполняет кеш категорий из БД
     */
   public void fillCashС() {
        String q = "select * from categories ORDER BY id ASC";
        ResultSet rSet=null;
        Statement stmt = null;
        try{
                stmt = db.get("categories").getDb().createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
                rSet = stmt.executeQuery(q);
        	while(rSet.next()){
                        Сategory c = new Сategory();
                        c.id = rSet.getInt(1);
                        c.category = rSet.getString(2);
                        c.category_ex = rSet.getString(3);
                        c.date = rSet.getTimestamp(4).getTime();
                        c.author_id = rSet.getString(5);
                        c.category_id = rSet.getInt(6);
                        c.state = rSet.getInt(7);
//                        byte[] d = rSet.getBytes(8);
//                        c.data = (Set<Integer>) TableObject.toObject(d);
                        c.init();
        		C.put(c.id, c);
                        CountC=c.id;
                        System.out.println("Cache categories "+c.category);
        	}
        } catch (Exception ex){
            logger.err(ex.getMessage());
        } finally {
        	if(rSet!=null) try{rSet.close();} catch(Exception e) {}
        	if(stmt!=null) try{stmt.close();} catch(Exception e) {}
        }
        //System.out.println("[essence] - Сache subjects OK!");
        logger.out("Cache categories OK!");
    }


    /**
     * Заполняет кеш форумов из БД
     */
    public void fillCashF() {
        String q = "select * from forums ORDER BY id ASC";
        ResultSet rst=null;
        Statement stmt = null;
        try{
        	stmt = db.get("forums").getDb().createStatement();
                rst = stmt.executeQuery(q);
        	while(rst.next()){
        		Forum f = new Forum();
        		f.id=rst.getInt(1);
        		f.forum=rst.getString(2);
        		f.forum_ex=rst.getString(3);
        		f.date=rst.getLong(4);
        		f.author_id=rst.getString(5);
                        f.category_id=rst.getInt(6);
                        f.state=rst.getInt(7);
        		F.put(f.id, f);
                        CountF=f.id;
                        System.out.println("Cache forums "+f.forum);
        	}
        } catch (Exception ex){
            ex.printStackTrace();
        } finally {
        	if(rst!=null) try{rst.close();} catch(Exception e) {};
        	if(stmt!=null) try{stmt.close();} catch(Exception e) {};
        }
        logger.out("Cache forums OK!");
    }

   /**
     * Заполняет кеш тем из БД
     */
    public void fillCashT() {
        String q = "select * from topics ORDER BY id ASC";
        ResultSet rst=null;
        Statement stmt = null;
        try{
        	stmt = db.get("topics").getDb().createStatement();
                rst = stmt.executeQuery(q);
        	while(rst.next()){
        		Topic t = new Topic();
        		t.id=rst.getInt(1);
        		t.topic=rst.getString(2);
        		t.topic_ex=rst.getString(3);
        		t.date=rst.getLong(4);
                        t.last_update=rst.getLong(5);
        		t.author_id=rst.getString(6);
                        t.forum_id=rst.getInt(7);
                        t.state=rst.getInt(8);
        		T.put(t.id, t);
                        CountT=t.id;
                        System.out.println("Cache topics "+t.topic);
        	}

        } catch (Exception ex){
            ex.printStackTrace();
        } finally {
        	if(rst!=null) try{rst.close();} catch(Exception e) {};
        	if(stmt!=null) try{stmt.close();} catch(Exception e) {};
        }
        logger.out("Cache topics OK!");
    }

   /**
    * Возвращает заданную категорию
    * @param id
    * @return
    */
    public Сategory getСategory(int id){
		if(!C.containsKey(id)) return null;
		return C.get(id);
    }

    /**
    * Возвращает заданный форум
    * @param id
    * @return
    */
    public Forum getForum(int id){
		if(!F.containsKey(id)) return null;
		return F.get(id);
    }

    /**
    * Возвращает заданный топик
    * @param id
    * @return
    */
    public Topic getTopic(int id){
		if(!T.containsKey(id)) return null;
		return T.get(id);
    }

   /**
    * Возвращает заданный пост
    * @param id
    * @return
    */
    public Post getPost(int post_id){
        if(P.containsKey(post_id)) return P.get(post_id);
        String query = "SELECT * FROM posts WHERE id = "+post_id;
        Post p = (Post)db.get("posts").getObject(new Post(), query);
        if (p.id == 0) return null;
        P.put(p.id, p);
        return p;
    }
 

   /**
    * Возвращает категории
    * @return
    */
    public LinkedHashMap <Integer,Сategory> getСategories(){
//        LinkedHashMap <Integer,Сategory> c = new LinkedHashMap <Integer,Сategory>();
//        Set<Integer> rid = C.keySet();
//        Integer[] categories=(Integer[])rid.toArray(new Integer[0]);
//        Arrays.sort(categories);
//          for(Integer i:categories){
//            c.put(i, C.get(i));
//          }
		return C;
    }

   /**
    * Возвращает форумы
    * @return
    */
    public LinkedHashMap <Integer,Forum> getForums(){
//        LinkedHashMap <Integer,Forum> f = new LinkedHashMap <Integer,Forum>();
//        Set<Integer> rid = F.keySet();
//        Integer[] forums=(Integer[])rid.toArray(new Integer[0]);
//        Arrays.sort(forums);
//          for(Integer i:forums){
//            f.put(i, F.get(i));
//          }
		return F;
    }

   /**
    * Возвращает темы
    * @return
    */
    public LinkedHashMap <Integer,Topic> getTopics(){
//        LinkedHashMap <Integer,Topic> t = new LinkedHashMap <Integer,Topic>();
//        Set<Integer> rid = T.keySet();
//        Integer[] topics=(Integer[])rid.toArray(new Integer[0]);
//        Arrays.sort(topics);
//          for(Integer i:topics){
//            t.put(i, T.get(i));
//          }
		return T;
    }
   /**
    * Возвращает форумы определенной категории
    * @param category_id ид категории
    * @return
    */
    public ArrayList <Forum> getListForums(int category_id){
        String query = "SELECT * FROM forums WHERE category_id = "+category_id+" AND state = 0";
        ArrayList list =db.get("forums").getObjectList(new Forum(), query);
		return list;
    }

//   /**
//    * Возвращает форумы определенной категории
//    * @param category_id ид категории
//    * @return
//    */
//    public LinkedHashMap <Integer,Forum> getHashForums(int category_id){
//        LinkedHashMap <Integer,Forum> f = new LinkedHashMap <Integer,Forum>();
//          for(Integer i:getForums().keySet()){
//             Forum forum = F.get(i);
//            if(forum.category_id==category_id)f.put(i, forum);
//          }
//		return f;
//    }

   /**
    * Возвращает темы определенного форума
    * @param forum_id ид форума
    * @return
    */
    public ArrayList <Topic> getListTopics(int forum_id){
        String query = "SELECT * FROM topics WHERE forum_id = "+forum_id+" AND state = 0";
        ArrayList list =db.get("topics").getObjectList(new Topic(), query);
		return list;
    }

   /**
    * Возвращает темы определенного форума
    * @param forum_id ид форума
    * @param first_index начальный индекс
    * @param count макс кол-во
    * @return
    */
    public ArrayList <Topic> getListTopics(int forum_id, int first_index, int count){
        // TODO подумать как сделать лучьше
         ArrayList <Topic> listTopics = getListTopics(forum_id);
         ArrayList <Topic> list = new ArrayList <Topic>();
         int c = 0;
         for(int index = first_index; index<listTopics.size(); index++){
             if (c>=count)break;
             list.add(listTopics.get(index-1));
             c++;
         }
         list.trimToSize();
         	return list;
    }

   /**
    * возвращает темы за определённый промежуток времени
    * @param time
    * @return
    */
    public ArrayList <Topic> getFreshListTopics(long time){
        String query = "SELECT * FROM topics WHERE `last_update` >= "+time+" AND `state` = 0 ORDER BY `last_update` DESC";
        ArrayList list =db.get("topics").getObjectList(new Topic(), query);
		return list;
    }

//   /**
//    * Возвращает темы определенного форума
//    * @param forum_id ид форума
//    * @return
//    */
//    public LinkedHashMap <Integer,Topic> getHashTopics(int forum_id){
//        LinkedHashMap <Integer,Topic> t = new LinkedHashMap <Integer,Topic>();
//          for(Integer i:getTopics().keySet()){
//             Topic topic = T.get(i);
//            if(topic.forum_id==forum_id)t.put(i, topic);
//          }
//		return t;
//    }

   /**
    * Возвращает сообщения определенной темы
    * @param topic_id ид темы
    * @return
    */
    public ArrayList <Post> getListPosts(int topic_id){
        String query = "SELECT * FROM posts WHERE topic_id = "+topic_id+" AND state = 0";
        ArrayList list = db.get("posts").getObjectList(new Post(), query);
		return list;
    }

   /**
    * Возвращает сообщения определенной темы
    * @param topic_id ид темы
    * @param first_index начальный индекс
    * @param count макс кол-во
    * @return
    */
    public ArrayList <Post> getListPosts(int topic_id, int first_index, int count){
        // TODO подумать как сделать лучьше
         ArrayList <Post> listPosts = getListPosts(topic_id);
         ArrayList <Post> list = new ArrayList <Post>();
         int c = 0;
         for(int index = first_index-1; index<listPosts.size(); index++){
             if (c>=count)break;
             list.add(listPosts.get(index));
             c++;
         }
         list.trimToSize();
         	return list;
    }
    /**
     * возвращает лист с ид тем на которые подписан пользователь
     * @param sn ид пользователя
     * @return
     */
    public ArrayList <Integer> getSubscriptions(String sn){
   	 return getUser(sn).subscription;
    }
    /**
     * возвращает лист с ид тем на которые подписан пользователь
     * @param u пользователь
     * @return
     */
    public ArrayList <Integer> getSubscriptions(User u){
   	 return u.subscription;
    }
    /**
     * возвращает лист тем на которые подписан пользователь
     * @param sn ид пользователя
     * @return
     */
    public ArrayList <Topic> getListSubscription(String sn){
         return getListSubscription(getUser(sn));
    }
    /**
     * возвращает лист тем на которые подписан пользователь
     * @param u пользователь
     * @return
     */
    public ArrayList <Topic> getListSubscription(User u){
         ArrayList <Topic> listTopics = new ArrayList<Topic>();
         for(int topic_id  : getSubscriptions(u)){
             Topic t = getTopic(topic_id);
             if (t!=null)
             listTopics.add(t);
         }
   	 return listTopics;
    }

    /**
     * добавить подписку на тему
     * @param sn ид пользователя
     * @param topic_id ид темы
     * @return
     */
    public boolean addSubscription(String sn,int topic_id){
   	 return  addSubscription(getUser(sn), topic_id);
    }

    /**
     * добавить подписку на тему
     * @param u пользователь
     * @param topic_id ид темы
     * @return
     */
    public boolean addSubscription(User u,int topic_id){
         if(u.id==0)return false;
         if (u.subscription.contains(topic_id))return false;
         u.subscription.add(topic_id);
         u.init();
         updateUser(u);
   	 return true;
    }
    /**
     * удалить подписку на тему
     * @param sn ид пользователя
     * @param topic_id ид темы
     * @return
     */
    public boolean delSubscription(String sn,int topic_id){
        return delSubscription(getUser(sn),topic_id);
    }
    /**
     * удалить подписку на тему
     * @param u пользователь
     * @param topic_id ид темы
     * @return
     */
    public boolean delSubscription(User u,int topic_id){
         if(u.id==0)return false;
         if (!u.subscription.contains(topic_id))return false;
         // TODO подумать как сделать лучьше
         int index = u.subscription.indexOf(topic_id);
//         Object o = topic_id;
         u.subscription.remove(index);
         u.init();
         updateUser(u);
   	 return true;
    }
    /**
     * возвращает лист пользователей подписанных на определённую тему
     * @param topic_id ид темы
     * @return
     */
    public ArrayList <User> getListUsersWithSubscription(int topic_id){
        // TODO подумать как сделать лучьше
        String query = "SELECT * FROM users WHERE state <> -1";
        ArrayList list = db.get("users").getObjectList(new User(), query);
        ArrayList <User> listUsers = new ArrayList <User>();
        for(User u : (ArrayList <User>)list){
            if(u.subscription.contains(topic_id))
                listUsers.add(u);
        }
	return listUsers;
    }
//   /**
//    * Возвращает сообщения определенной темы
//    * @param topic_id ид темы
//    * @return
//    */
//    public LinkedHashMap <Integer,Post> getPosts(int topic_id){
//        if (P.containsKey(topic_id))return P.get(topic_id);
//	return CashP(topic_id);
//    }
//
//    /**
//     * Заполняет кеш сообщений определенной темы из БД
//     */
//    public LinkedHashMap <Integer,Post> CashP(int topic_id) {
//        String posts_db = "posts_"+topic_id;
//        LinkedHashMap <Integer,Post> pp = new LinkedHashMap <Integer,Post>();
//        if (!db.containsKey(posts_db))return pp;
//        String q = "select * from posts where topic_id=" + topic_id+";";
//        ResultSet rst=null;
//        Statement stmt = null;
//        try{
//        	stmt = db.get(posts_db).getDb().createStatement();
//                rst = stmt.executeQuery(q);
//                int post_id=1;
//        	while(rst.next()){
//            //        if (rst.getInt(7)==STATE_NORMAL){
//        		Post p = new Post();
//        		p.id=rst.getInt(1);
//        		p.post=rst.getString(2);
//        		p.date=rst.getLong(3);
//        		p.author_id=rst.getString(4);
//                        p.topic_id=rst.getInt(5);
//        		p.reputation=rst.getInt(6);
//                        p.state=rst.getInt(7);
//                        p.init();
//                        pp.put(post_id, p);
//                        post_id++;
//               //     }
//        	}
//                P.put(topic_id, pp);
//        } catch (Exception ex){
//            ex.printStackTrace();
//        } finally {
//        	if(rst!=null) try{rst.close();} catch(Exception e) {};
//        	if(stmt!=null) try{stmt.close();} catch(Exception e) {};
//        }
//        logger.out("Сache "+posts_db+" OK!");
//        return pp;
//    }


   /**
    * Обновление данных о категории
    * @param с
    */
    public void updateСategory(Сategory c) {
          db.get("сategories").updateObject(c);
          if (C.containsKey(c.id))C.put(c.id, c);
    }

   /**
    * Обновление данных о форуме
    * @param f
    */
    public void updateForum(Forum f) {
	 db.get("forums").updateObject(f);
         if (F.containsKey(f.id))F.put(f.id, f);
    }

  /**
    * Обновление данных о теме
    * @param t
    */
    public void updateTopic(Topic t) {
	db.get("topics").updateObject(t);
        if (T.containsKey(t.id))T.put(t.id, t);
    }
/*************работа с пользователями*****************/

    /**
     * Определение группы пользователя
     */
    public String getUserGroup(String sn){
        if(uc.containsKey(sn)){
        	return uc.get(sn).user_group;
        }
        User u = getUserFromDB(sn);
        if(u.id==0){
        	u.sn = sn;
                u.init();
        	uc.put(u.sn, u);
         } else {
        	uu.put(u.id, u.sn);
        	uc.put(u.sn, u);
         }
        	return u.user_group;
    }
     /**
     * Возвращает пользователя из БД
     * @param id - ИД пользователя
     * @return
     */
    private User getUserFromDB(int id){
    	ArrayList list = db.get("users").getObjectList(new User(),"select * from users where id='"+id+"'");
    	User u = new User();
    	if(list.size()>0) u = (User)list.get(0);
    	return u;
    }

     /**
     * Возвращает пользователя из БД
     * @param sn - УИН пользователя
     * @return
     */
    private User getUserFromDB(String sn){
    	ArrayList list = db.get("users").getObjectList(new User(),"select * from users where sn='"+sn+"'");
    	User u = new User();
    	if(list.size()>0) u = (User)list.get(0);
    	return u;
    }
    /**
     * Определение зарегистрирован пользователь или нет
     * @param sn
     * @return
     */
    public boolean testUnregUser(String sn){
        if(uc.containsKey(sn)){
        	return uc.get(sn).state!=0;
        }
        User u = getUserFromDB(sn);
        if(u.id==0){
        	u.sn = sn;
                u.init();
        	uc.put(u.sn, u);
        	return false;
        } else {
        	uu.put(u.id, u.sn);
        	uc.put(u.sn, u);
        	return true;
        }
    }

    public User getUser(int id) {
    	if(uu.containsKey(id)){
    		return uc.get(uu.get(id));
    	}
    	User u = getUserFromDB(id);
    	if(u.id==0) return u; // Нет юзера в БД, или глюк :)
    	uu.put(id, u.sn);
    	uc.put(u.sn, u);
    	return u;
    }


    public User getUser(String uin) {
    	if(uc.containsKey(uin)) return uc.get(uin);
    	User u = getUserFromDB(uin);
    	if(u.id==0) return u; // Нет юзера в БД, или глюк :)
    	uu.put(u.id, u.sn);
    	uc.put(u.sn, u);
    	return u;
    }

    /**
     * добавить пользователя
     * @param u
     * @return
     */
    public synchronized int addUser(User u) {
        if (testUnregUser(u.sn))return getUser(u.sn).id;
        u.id = getCountUsers()+1;
        u.state = USER_STATE_NO_FORUM;
        u.init();//не забывать обновлять после изменений
        uc.put(u.sn,u); //Кэшируем юзера
        db.get("users").insertObject(u);
        CountU=u.id;
        logger.db("Add new user: ["+u.id+"] ["+u.sn+"] "+u.nick);
        return CountU;
    }


    public void updateUser(User u) {
        uc.put(u.sn,u); //кэшируем юзера
        db.get("users").updateObject(u);
    }


    public HashMap<String,Object> getUserProp(String sn){
          return getUser(sn).other_prop;
    }

    public Object getUserProp(int id,String key){
          return getUserProp(getUser(id),key);
    }

    public Object getUserProp(String sn,String key){
          return getUserProp(getUser(sn),key);
    }

    public Object getUserProp(User u,String key){
          if(u.other_prop.containsKey(key))
          return u.other_prop.get(key);
          else
          return null;
    }

    public Topic getLastTopic(String sn) {
        return getLastTopic(getUser(sn));
    }

    public Topic getLastTopic(User u) {
        Topic t = getTopic(u.last_topic_id);
        return t==null ? new Topic() : t;
    }

    public boolean setUserPlace(String sn,int forum_id,int topic_id) {
        return setUserPlace(sn, 0, forum_id, topic_id);
    }

    public boolean setUserPlace(String sn,int category_id,int forum_id,int topic_id) {
        User u = getUser(sn);
        if (u.id==0) return false;
        if(u.category_id == category_id&&u.forum_id == forum_id&&u.topic_id == topic_id)return true;
        u.category_id = category_id;
        u.forum_id = forum_id;
        u.topic_id = topic_id;
        if (topic_id!=0)u.last_topic_id = topic_id;
        u.init();
        updateUser(u);
        return true;
    }
/*************работа с категориями, форумами, темами, сообщениями**************/


    /**
     * Создание категории
     * @param c
     * @return
     */
    public int createСategory(Сategory c){
        CountC++;
        c.id = CountC;
        c.init();//не забывать обновлять после изменений
        C.put(c.id, c);
        db.get("categories").insertObject(c);
        logger.db("Add new category: ["+c.id+"] "+c.category);
        return CountC;
    }

    /**
     * Создание форума
     * @param f
     * @return
     */

    public int createForum(Forum f){
        CountF++;
        f.id = CountF;
        f.init();//не забывать обновлять после изменений
        F.put(f.id, f);
        db.get("forums").insertObject(f);
        logger.db("Add new forum: ["+f.id+"] "+f.forum);
        return CountF;
    }

    /**
     * Создание темы
     * @param t
     * @return
     */
    public int createTopic(Topic t){
        CountT++;
        t.id = CountT;
        t.init();//не забывать обновлять после изменений
        T.put(t.id, t);
        db.get("topics").insertObject(t);
        logger.db("Add new topic: ["+t.id+"] "+t.topic);
        return CountT;
    }


    /**
     * Создание сообщения
     * @param p
     * @return
     */
    public int createPost(Post p){
        p.id = getCountPosts()+1;
        p.init();//не забывать обновлять после изменений
        db.get("posts").insertObject(p);
        logger.db("Add new post: ["+p.id+"] "+p.post);
        CountP++;
        Topic t = getTopic(p.topic_id);
        if (t.id!=0){
            t.last_update = p.date;
            t.init();
            updateTopic(t);
        }
        return CountP;
    }

}
